package cn.lxb.web.starter.template.helper;

import cn.lxb.web.starter.template.constant.Format;
import cn.lxb.web.starter.template.constant.Formats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * 时间助手类，使用标准"yyyy-MM-dd HH:mm:ss"格式和"yyyy-MM-dd"短标记格式
 *
 * @author 54lxb
 * @since 2019-11-20
 */
public final class TimeHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(TimeHelper.class);

    private static final Map<Integer, Format> FORMATS;

    private static final int PATTERN_CACHE_SIZE = 500;

    private static final ConcurrentMap<String, DateTimeFormatter> FORMATTER_CACHE = new ConcurrentHashMap<>();

    static {
        FORMATS = Arrays.stream(Formats.values()).collect(Collectors.toMap(Formats::getLength, format -> format));
    }

    private TimeHelper() {
    }

    /**
     * 日期类型对象格式化
     *
     * @param date    日期类型对象
     * @param pattern 模式
     * @return 格式化后的文本内容
     */
    public static String format(final Date date, String pattern) {
        if (date == null || StringUtils.isEmpty(pattern)) {
            return null;
        }
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        return formatter.format(localDateTime);
    }

    /**
     * 日期类型对象格式化
     *
     * @param localDateTime 日期类型对象
     * @param pattern       模式
     * @return 格式化后的文本内容
     */
    public static String format(final LocalDateTime localDateTime, String pattern) {
        if (localDateTime == null || StringUtils.isEmpty(pattern)) {
            return null;
        }

        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期类型对象格式化成全时间格式
     *
     * @param date 日期类型对象
     * @return 全时间格式的日期内容文本
     */
    public static String format(final Date date) {
        return format(date, Formats.STANDARD_DATE.getFormat());
    }

    /**
     * 日期类型对象格式化成全时间格式
     *
     * @param localDateTime 日期类型对象
     * @return 全时间格式的日期内容文本
     */
    public static String format(final LocalDateTime localDateTime) {
        return format(localDateTime, Formats.STANDARD_DATE.getFormat());
    }

    /**
     * 日期类型对象格式化成只包含日期格式
     *
     * @param date 日期类型对象
     * @return 只包含日期格式的日期内容文本
     */
    public static String formatDate(final Date date) {
        return format(date, Formats.SIMPLE_DATE.getFormat());
    }

    /**
     * 日期类型对象格式化成只包含日期格式
     *
     * @param localDateTime 日期类型对象
     * @return 只包含日期格式的日期内容文本
     */
    public static String formatDate(final LocalDateTime localDateTime) {
        return format(localDateTime, Formats.SIMPLE_DATE.getFormat());
    }

    /**
     * 通过时间字符串解析生成日期对象
     *
     * @param string 时间字符串
     * @return 日期对象
     */
    public static Date parse(final String string) {
        if (StringUtils.isEmpty(string)) {
            return null;
        }
        String text = string.trim();
        Date date = numberParse(text);
        if (date != null) {
            return date;
        }
        return textParse(text);
    }

    /**
     * 通过时间字符串解析生成日期对象
     *
     * @param string 时间字符串
     * @return 日期时间对象
     */
    public static LocalDateTime parseLocalDateTime(final String string) {
        return toLocalDateTime(parse(string));
    }

    /**
     * 将LocalDateTime转换为Date
     *
     * @param localDateTime LocalDateTime
     * @param zoneId        时区ID
     * @return Date
     */
    public static Date toDate(final LocalDateTime localDateTime, final ZoneId zoneId) {
        if (localDateTime == null) {
            return null;
        }
        return Date.from(localDateTime.atZone(getZoneId(zoneId)).toInstant());
    }

    /**
     * 使用默认时区将LocalDateTime转换为Date
     *
     * @param localDateTime LocalDateTime、
     * @return Date
     */
    public static Date toDate(final LocalDateTime localDateTime) {
        return toDate(localDateTime, null);
    }

    /**
     * 将Date转换为DateLocalDateTime
     *
     * @param date   Date
     * @param zoneId 时区ID
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(final Date date, final ZoneId zoneId) {
        if (date == null) {
            return null;
        }
        return LocalDateTime.ofInstant(date.toInstant(), getZoneId(zoneId));
    }

    /**
     * 使用默认时区将Date转换为DateLocalDateTime
     *
     * @param date Date
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(final Date date) {
        return toLocalDateTime(date, null);
    }

    /**
     * 格式化字符串转为Date
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return Date
     */
    public static Date parseDate(String time, String pattern) {
        if (Formats.SIMPLE_DATE.getFormat().equals(pattern)) {
            return Date.from(parseLocalDate(time, pattern).atTime(0, 0).atZone(ZoneId.systemDefault()).toInstant());
        }
        return Date.from(parseLocalDateTime(time, pattern).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 格式化字符串转为LocalDateTime
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return LocalDateTime
     */
    public static LocalDateTime parseLocalDateTime(String time, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return LocalDateTime.parse(time, formatter);
    }

    /**
     * 格式化字符串转为LocalDateTime
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return LocalDateTime
     */
    public static LocalDate parseLocalDate(String time, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return LocalDate.parse(time, formatter);
    }

    private static Date textParse(final String text) {
        String timeText = text.replace('T', ' ').replace("Z", "");
        Format format = FORMATS.get(timeText.length());
        if (format == null) {
            return null;
        }
        try {
            return parseDate(timeText, format.getFormat());
        } catch (Exception e) {
            LOGGER.debug(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 在缓存中创建DateTimeFormatter
     *
     * @param pattern 格式
     * @return DateTimeFormatter
     */
    private static DateTimeFormatter createCacheFormatter(String pattern) {
        if (pattern == null || pattern.length() == 0) {
            throw new IllegalArgumentException("Invalid pattern specification");
        }
        DateTimeFormatter formatter = FORMATTER_CACHE.get(pattern);
        if (null != formatter) {
            return formatter;
        }
        if (FORMATTER_CACHE.size() < PATTERN_CACHE_SIZE) {
            formatter = new DateTimeFormatterBuilder().appendPattern(pattern)
                    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                    .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
                    .toFormatter();
            DateTimeFormatter oldFormatter = FORMATTER_CACHE.putIfAbsent(pattern, formatter);
            formatter = null != oldFormatter ? oldFormatter : formatter;
        }
        return formatter;
    }

    private static ZoneId getZoneId(final ZoneId zoneId) {
        return zoneId == null ? ZoneId.systemDefault() : zoneId;
    }

    private static Date numberParse(final String text) {
        try {
            long timestamp = Long.parseLong(text);
            return new Date(timestamp);
        } catch (Exception e) {
            LOGGER.trace(e.getMessage(), e);
            return null;
        }
    }
}
